package q1.project.common.config;

import com.github.benmanes.caffeine.cache.Caffeine;
import java.time.Duration;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * 缓存配置类
 *
 * <p>提供 Redis 和 Caffeine（本地缓存）两种缓存支持。 Redis 用于分布式缓存，Caffeine 用于本地缓存。
 *
 * <p>缓存策略： - 热点数据：使用 Caffeine 本地缓存（快速访问） - 共享数据：使用 Redis 分布式缓存（多实例共享） - 配置数据：使用 Caffeine（配置变更不频繁）
 *
 * @author Q1 Team
 * @version 1.0.0
 * @since 2025-11-05
 */
@Configuration
@EnableCaching
public class CacheConfig {

  /**
   * Redis 缓存管理器（主缓存）
   *
   * <p>用于分布式缓存，适合多实例部署场景。
   *
   * @param connectionFactory Redis 连接工厂
   * @return Redis 缓存管理器
   */
  @Bean
  @Primary
  @ConditionalOnMissingBean(name = "redisCacheManager")
  public CacheManager redisCacheManager(RedisConnectionFactory connectionFactory) {
    // Redis 缓存配置
    RedisCacheConfiguration config =
        RedisCacheConfiguration.defaultCacheConfig()
            // 序列化方式
            .serializeKeysWith(
                RedisSerializationContext.SerializationPair.fromSerializer(
                    new StringRedisSerializer()))
            .serializeValuesWith(
                RedisSerializationContext.SerializationPair.fromSerializer(
                    new GenericJackson2JsonRedisSerializer()))
            // 缓存过期时间（默认 1 小时）
            .entryTtl(Duration.ofHours(1))
            // 不允许缓存 null 值
            .disableCachingNullValues()
            // 缓存键前缀
            .prefixCacheNameWith("cache:");

    // 创建缓存管理器
    return RedisCacheManager.builder(connectionFactory)
        .cacheDefaults(config)
        // 可以为不同的缓存名称设置不同的过期时间
        .withCacheConfiguration("user", config.entryTtl(Duration.ofMinutes(30)))
        .withCacheConfiguration("permission", config.entryTtl(Duration.ofHours(2)))
        .withCacheConfiguration("tenant", config.entryTtl(Duration.ofHours(1)))
        .withCacheConfiguration("post", config.entryTtl(Duration.ofMinutes(10)))
        .transactionAware()
        .build();
  }

  /**
   * Caffeine 本地缓存管理器（二级缓存）
   *
   * <p>用于本地缓存，适合热点数据和配置数据。 本地缓存速度快，但只能在本实例内共享。
   *
   * @return Caffeine 缓存管理器
   */
  @Bean("caffeineCacheManager")
  public CacheManager caffeineCacheManager() {
    CaffeineCacheManager cacheManager = new CaffeineCacheManager();

    // Caffeine 缓存配置
    Caffeine<Object, Object> caffeine =
        Caffeine.newBuilder()
            // 最大缓存条目数
            .maximumSize(10000)
            // 写入后过期时间（5 分钟）
            .expireAfterWrite(5, TimeUnit.MINUTES)
            // 访问后过期时间（3 分钟）
            .expireAfterAccess(3, TimeUnit.MINUTES)
            // 弱引用 key（GC 时可回收）
            .weakKeys()
            // 弱引用 value（GC 时可回收）
            .weakValues()
            // 记录缓存统计信息
            .recordStats();

    cacheManager.setCaffeine(caffeine);

    // 设置缓存名称
    cacheManager.setCacheNames(Arrays.asList("config", "hotData", "userInfo"));

    return cacheManager;
  }
}
